import { getPropertyControlTypes } from "components/propertyControls";
import type {
  ValidationResponse,
  ValidationTypes,
} from "constants/WidgetValidation";
import type { CodeEditorExpected } from "components/editorComponents/CodeEditor";
import type { UpdateWidgetPropertyPayload } from "actions/controlActions";
import type { AdditionalDynamicDataTree } from "utils/autocomplete/customTreeTypeDefCreator";
import type { Stylesheet } from "entities/AppTheming";
import type { ReduxActionType } from "actions/ReduxActionTypes";
import type { PropertyUpdates } from "WidgetProvider/types";
import type { WidgetProps } from "widgets/BaseWidget";
import type { EvaluationSubstitutionType } from "constants/EvaluationConstants";

const ControlTypes = getPropertyControlTypes();

export type ControlType = (typeof ControlTypes)[keyof typeof ControlTypes];

export interface PropertyPaneSectionConfig {
  /**
   * Title displayed at the top of a collapsible section in the property pane.
   */
  sectionName: string;
  /**
   * Unique identifier for the section. Used for:
   * - Managing section collapse/expand state in Redux
   * - Navigation and search functionality in the property pane
   * - React rendering optimization (as key)
   *
   * If not provided, it will be auto-generated during widget initialization.
   */
  id?: string;
  /**
   * Array of properties that should go inside the section.
   */
  children: PropertyPaneConfig[];
  /**
   * If false, the section will not be collapsible.
   */
  collapsible?: boolean;
  /**
   * A unique id generated by combining the ids of all the children.
   * Mainly used in memoization to prevent unnecessary re-renders of property sections ( PropertySection.tsx )
   */
  childrenId?: string;
  /**
   * Callback function to determine if the section should be hidden.
   *
   * @param props - Current widget properties
   * @param propertyPath - Path to the widget property
   * @returns - True if the section should be hidden, false otherwise
   */
  // TODO: Fix this the next time the file is edited
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  hidden?: (props: any, propertyPath: string) => boolean;
  /**
   * @param props - Current widget properties
   * @param propertyPath - Path to the widget property
   * @returns - True if the section should be disabled, false otherwise
   */
  // TODO: Fix this the next time the file is edited
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  shouldDisableSection?: (props: any, propertyPath: string) => boolean;
  /**
   * Help text to show when section is disabled.
   * Appears as a tooltip when hovering over the disabled section.
   */
  disabledHelpText?: string;
  /**
   * when true, the section will be open by default.
   * Note: Seems like this is not used anywhere.
   */
  isDefaultOpen?: boolean;
  propertySectionPath?: string;
  /**
   * Used to show a tag right after the section name (usedonly in the search results)
   */
  tag?: string;
  /**
   * Indicates whether this section contains properties that need to be generated dynamically at runtime.
   * Used in conjunction with generateDynamicProperties.
   *
   * Common use cases:
   * - Dynamic event handlers ( CustomWidget's )
   * - Properties that depend on user configuration
   * - Properties that need to be generated based on widget state
   */
  hasDynamicProperties?: boolean;
  /**
   * Function to generate property controls dynamically at runtime.
   * Called when hasDynamicProperties is true.
   *
   * @param widget - Current widget properties
   * @returns Array of dynamically generated property controls
   *
   * @example
   * Used in CustomWidget and ExternalWidget to dynamically generate event handler properties
   * based on available events.
   */
  generateDynamicProperties?: (
    widget: WidgetProps,
  ) => PropertyPaneControlConfig[];
  expandedByDefault?: boolean;
}

export interface PanelConfig {
  editableTitle: boolean;
  titlePropertyName: string;
  panelIdPropertyName: string;
  children?: PropertyPaneConfig[];
  contentChildren?: PropertyPaneConfig[];
  styleChildren?: PropertyPaneConfig[];
  searchConfig?: PropertyPaneConfig[]; // A combination of contentChildren and contentChildren which will be used to display search results
  updateHook: (
    // TODO: Fix this the next time the file is edited
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    props: any,
    propertyPath: string,
    // TODO: Fix this the next time the file is edited
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    propertyValue: any,
  ) => Array<PropertyUpdates> | undefined;
}

export interface PropertyPaneControlConfig {
  /**
   * Unique identifier for the control. Used for internal tracking and debugging.
   * It added by `addPropertyConfigIds` function in `WidgetProvider/factory/helpers.ts`.
   */
  id?: string;
  /**
   * Label shown above the control.
   */
  label: string;
  /**
   * Human-readable slugified name of the property. Used to identify the property in the widget properties.
   */
  propertyName: string;
  /**
   * Used to provide more context about the property. Appears as tooltip when we hover over the label of the property.
   * Note: This is different from `helperText` which appears below the property input.
   */
  helpText?: string;
  /**
   * Dynamic text that appears below the property input.
   */
  helperText?: ((props: unknown) => React.ReactNode) | React.ReactNode;
  /**
   * If true, transform the property input into plain input where js can be written..
   */
  isJSConvertible?: boolean;
  /**
   * Used in special cases where we want to custom control instead regular text input control.
   * For example: `COMPUTE_VALUE` control is used in table widget provides access to currentRow in controls in the table columns.
   */
  customJSControl?: string;
  /**
   * Type of the property control.
   * Full list of supported controls can be found in `app/client/src/components/propertyControls/index.ts`.
   */
  controlType: ControlType;
  /** @deprecated. Not used anywhere. */
  validationMessage?: string;
  /**
   * path that is used to get evaluated value for the property.
   */
  dataTreePath?: string;
  /**
   * There is no requirement to define children for a control. This is just done to suffice types
   * in places where controlConfig and sectionConfig are not differentiated.
   */
  children?: PropertyPaneConfig[];
  panelConfig?: PanelConfig;
  /**
   * Callback function to update related widget properties.
   *
   * @param propertyName - Path to the widget property
   * @param propertyValue - New value of the property
   * @param props - Current widget properties
   * @returns - Array of property updates
   *
   * @example Used in tabs widget to update the label of the tab.
   */
  updateRelatedWidgetProperties?: (
    propertyName: string,
    // TODO: Fix this the next time the file is edited
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    propertyValue: any,
    // TODO: Fix this the next time the file is edited
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    props: any,
  ) => UpdateWidgetPropertyPayload[];
  /**
   * Function that is called when the property is updated, it is mainly used to update other properties
   *
   * @param props - Current widget properties
   * @param propertyName - Path to the widget property
   * @param propertyValue - New value of the property
   *
   * @returns - Array of property updates
   */
  updateHook?: (
    // TODO: Fix this the next time the file is edited
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    props: any,
    propertyName: string,
    // TODO: Fix this the next time the file is edited
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    propertyValue: any,
  ) => Array<PropertyUpdates> | undefined;
  /**
   * callback function to determine if the property should be hidden.

   * @param props - Current widget properties
   * @param propertyPath - Path to the widget property
   * @returns - True if the property should be hidden, false otherwise
   */
  // TODO: Fix this the next time the file is edited
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  hidden?: (props: any, propertyPath: string) => boolean;
  /**
   * callback function to determine if the property should be disabled.

   * @param props - Current widget properties
   * @param propertyPath - Path to the widget property
   * @returns - True if the property should be disabled, false otherwise
   */
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  shouldDisableSection?: (props: any, propertyPath: string) => boolean;
  /**
   * Help text to show when property is disabled.
   * Appears as a tooltip when hovering over the disabled property.
   */
  disabledHelpText?: string;
  /**
   * If true, the property is hidden.
   * Note: hidden and invisible do the same thing but differently. hidden uses a callback to determine if the property should be hidden.
   * invisible is a boolean flag to hide the property.
   */
  invisible?: boolean;
  isBindProperty: boolean;
  /**
   * If true, it means the property triggers a widget action.
   *
   * @example
   * OnClick property in Button widget is a trigger property that can trigger an action.
   */
  isTriggerProperty: boolean;
  /**
   * Validation configuration for the property
   */
  validation?: ValidationConfig;
  /**
   * Callback function to provide additional autocomplete data for the autocomplete list.
   *
   * @param props - Current widget properties
   * @returns - Additional autocomplete data
   */
  // TODO: Fix this the next time the file is edited
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  additionalAutoComplete?: (props: any) => AdditionalDynamicDataTree;
  evaluationSubstitutionType?: EvaluationSubstitutionType;
  /**
   * All properties that current property is dependent on.
   * All of these properties becomes available in `widgetProperties` in the property control..
   */
  dependencies?: string[];
  /**
   * It is same as `dependencies` but these dependencies are not statically defined in the widget.
   * The callback is called  during rendering of the property control to get the latest dependencies.
   *
   * @example
   * Used in CustomWidget to dynamically get the dependencies based on the current state of the widget.
   */
  dynamicDependencies?: (widget: WidgetProps) => string[];
  /**
   * Dependencies to be picked from the __evaluated__ object
   */
  evaluatedDependencies?: string[];
  expected?: CodeEditorExpected;
  /**
   * Used to get value of the property from stylesheet config. Used in app theming v1 ( Not needed in anvil )
   *
   * @param props - Current widget properties
   * @param propertyPath - Path to the widget property
   * @param stylesheet - Stylesheet config
   * @returns - Value of the property
   */
  getStylesheetValue?: (
    // TODO: Fix this the next time the file is edited
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    props: any,
    propertyPath: string,
    stylesheet?: Stylesheet,
  ) => Stylesheet[string];
  /**
   * Options for certain controls like Dropdown Control
   * Note: This should be moved to controlConfig instead of being a top level property.
   */
  // TODO(abhinav): To fix this, rename the options property of the controls which use this
  // Alternatively, create a new structure
  // TODO: Fix this the next time the file is edited
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  options?: any;
  // The following should ideally be used internally
  postUpdateAction?: ReduxActionType;
  /**
   * If true, the property is a panel property ( that is nested property pane )
   */
  isPanelProperty?: boolean;
  // Switch mode ( JS -> Text )
  shouldSwitchToNormalMode?: (
    isDynamic: boolean,
    isToggleDisabled: boolean,
    triggerFlag?: boolean,
  ) => boolean;
  /**
   * `controlConfig` is a generic record that can be used to pass additional configuration
   * options to the property control. The specific structure and contents of this record
   * will depend on the control type and its individual requirements.
   */
  controlConfig?: Record<string, unknown>;
  /**
   * The default value of the property.
   */
  defaultValue?: unknown;
  /**
   * If the property is marked reusable, on the next drop it will use the value of the last dropped widget.
   */
  isReusable?: boolean;
}

interface ValidationConfigParams {
  /**
   * Minimum allowed value for a number.
   */
  min?: number;
  /**
   * Maximum allowed value for a number.
   */
  max?: number;
  /**
   * If true, the value must be a positive integer.
   */
  natural?: boolean;
  /**
   * Default value for any type.
   */
  default?: unknown;
  /**
   * If true or an array of strings, the value must be unique in an array.
   * If an array of strings is provided, it specifies particular paths that must be unique.
   */
  unique?: boolean | string[];
  /**
   * If true, the value is required.
   * Now used to check if the value is an empty string.
   */
  required?: boolean;
  /**
   * If true, the key is required.
   */
  requiredKey?: boolean;
  /**
   * Validator regex for text type.
   */
  regex?: RegExp;
  /**
   * Allowed keys in an object type.
   */
  allowedKeys?: Array<{
    name: string;
    type: ValidationTypes;
    params?: ValidationConfigParams;
  }>;
  /**
   * Allowed values in a string and array type.
   */
  allowedValues?: unknown[];
  /**
   * Children configurations in an ARRAY or OBJECT_ARRAY type.
   */
  children?: ValidationConfig;
  /**
   * Validation function for FUNCTION type.
   *
   * @param value - The value to validate
   * @param props - Current widget properties
   * @param _ - Additional parameter (unused)
   * @param moment - Moment.js instance
   * @returns - Validation response
   */
  fn?: (
    value: unknown,
    // TODO: Fix this the next time the file is edited
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    props: any,
    // TODO: Fix this the next time the file is edited
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    _?: any,
    // TODO: Fix this the next time the file is edited
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    moment?: any,
  ) => ValidationResponse;
  /**
   * AUTO GENERATED, SHOULD NOT BE SET BY WIDGET DEVELOPER
   */
  fnString?: string;
  /**
   * FUNCTION type expected type and example.
   */
  expected?: CodeEditorExpected;
  /**
   * If true, enables strict string validation of TEXT type.
   */
  strict?: boolean;
  /**
   * If true, ignores the case of keys.
   */
  ignoreCase?: boolean;
  /**
   * Used for ValidationType.ARRAY_OF_TYPE_OR_TYPE to define sub type.
   */
  type?: ValidationTypes;
  /**
   * Used for ValidationType.UNION to define sub types.
   */
  types?: ValidationConfig[];
  /**
   * Used for ValidationType.ARRAY_OF_TYPE_OR_TYPE to define sub type params.
   */
  params?: ValidationConfigParams;
  /**
   * Used for ValidationType.NUMBER to allow 0 to be passed through. Default value is true.
   */
  passThroughOnZero?: boolean;
  /**
   * Used for ValidationType.TEXT to limit line breaks in a large JSON object.
   */
  limitLineBreaks?: boolean;
  /**
   * Used for ValidationType.UNION when none of the union type validations succeed.
   */
  defaultValue?: unknown;
  /**
   * Used for ValidationType.UNION when none of the union type validations succeed.
   */
  defaultErrorMessage?: string;
}

export interface ValidationConfig {
  type: ValidationTypes;
  params?: ValidationConfigParams;
}

export type PropertyPaneConfig =
  | PropertyPaneSectionConfig
  | PropertyPaneControlConfig;

export interface ActionValidationConfigMap {
  [configProperty: string]: ValidationConfig;
}
